/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.core.session;

import io.iec.edp.caf.core.session.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.session.SessionRepository;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.List;

/**
 * Session服务接口实现类
 *
 * @author wangyandong
 * @date 2019/12/14 15:32
 */
@Slf4j
public class CafSessionServiceImpl implements ICafSessionService {

    private CafSessionManager manager;

    public CafSessionServiceImpl(SessionRepository repo) {
        this.manager = new CafSessionManager(repo);
    }

    @Override
    public CafSession create(Integer tenantId, String userId, String language, HashMap<String, String> items, SessionType sessionType) {
        return this.manager.create(tenantId, userId, null, null, language, items, sessionType);
    }

    @Override
    public CafSession create(Integer tenantId, String userId, String userCode, String userName, String language, HashMap<String, String> items, SessionType sessionType) {
        if (log.isDebugEnabled())
            log.debug("Create Session, StackTrace: {}", Utils.stackTrace());
        return this.manager.create(tenantId, userId, userCode, userName, language, items, sessionType);
    }

    @Override
    public CafSession findById(String sessionId) {
        if (log.isDebugEnabled())
            log.debug("Find Session {}, StackTrace: {}", sessionId, Utils.stackTrace());
        Assert.hasText(sessionId, "sessionId can not be null");

        //获取当前上下文session
        CafSession currentSession = getCurrentSession();
        if (currentSession != null && sessionId.equals(currentSession.getId()))
            return currentSession;
        return this.manager.findById(sessionId);
    }

    @Override
    public void update(CafSession session) {
        if (log.isDebugEnabled())
            log.debug("Update Session {}, StackTrace: {}", session != null ? session.getId() : null, Utils.stackTrace());
        this.manager.update(session);
    }

    @Override
    public void deleteById(String sessionId) {
        if (log.isDebugEnabled())
            log.debug("Delete Session {}, StackTrace: {}", sessionId, Utils.stackTrace());
        Assert.hasText(sessionId, "sessionId can not be null");

        this.manager.deleteById(sessionId);
    }

    @Override
    public boolean isExpired(String sessionId) {
        if (log.isDebugEnabled())
            log.debug("IsExpired Session {}，StackTrace: {}", sessionId, Utils.stackTrace());
        Assert.hasText(sessionId, "sessionId can not be null");

        /**
         * 1.Compare the current session with the incoming session,
         * 2.if first step is not sure,use redis to get session object determine
         * first step is improve performance avoid frequent queries for Redis
         *
         * in most time this method can correctly judge whether the session is expired,
         * but sometime when biz code shared one session(backend session/web session) in multiple threads
         * if one thread complete and delete the session,other threads will error to use the session,but
         * this method(isExpired) will still return false(because the first step in one not complete thread
         * will return false), in this case must through the log file to found delete session log to analysis.
         * */
        CafSession currentSession = getCurrentSession();
        if (currentSession != null && sessionId.equals(currentSession.getId()))
            return false;
        return this.manager.isExpired(sessionId);
    }

    @Override
    public List<String> filterExpired(List<String> sessionIds) {
        if (log.isDebugEnabled())
            log.debug("FilterExpired Sessions {}，StackTrace: {}", sessionIds, Utils.stackTrace());
        Assert.notNull(sessionIds, "sessionIds can not be null");

        return this.manager.filterExpired(sessionIds);
    }

    /**
     * 获取当前上下文Session
     */
    private CafSession getCurrentSession() {
        return CafSessionContextHolder.getCurrentSession();
    }

}
